
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <fcntl.h>
#include <unistd.h>

#include <libifconfig.h>

#include <libepoll.h>

#include <iostream>
#include <list>

#define FILE_MAX_LEN				1024

enum client_type
{
	CLIENT_TYPE_NORMAL = 0,

	CLIENT_TYPE_MAIN,

	CLIENT_TYPE_SERV_1,

	CLIENT_TYPE_PIPE,

	CLIENT_TYPE_TCP_BUTT
};

enum pipe_type
{
	PIPE_TYPE_UNKOWN = 0,

	PIPE_TYPE_SERVER,
	PIPE_TYPE_CLIENT,

	PIPE_TYPE_BUTT
};

struct client_tcp_listener
{
	char eth[32];

	char ip[32];
	unsigned short port;
};

struct client_pipe
{
	enum pipe_type type;

	int pipe_fd;
};

static struct client_list
{
	enum client_type type;

	void *p;
} handle_list[FILE_MAX_LEN];

struct pipe_node
{
	char xcode[64];

	int fd;
};

static std::list<struct pipe_node> m_pipe;

static bool find_node(const char *xcode, int fd)
{
	bool ret = false;

	for(std::list<struct pipe_node>::iterator iter = m_pipe.begin(); iter != m_pipe.end(); ++iter)
	{
		if(0 == strcmp(xcode, iter->xcode))
		{
			struct client_pipe *pipe = (struct client_pipe *)handle_list[iter->fd].p;

			pipe->pipe_fd = fd;

			pipe = (struct client_pipe *)handle_list[fd].p;

			pipe->pipe_fd = iter->fd;

			m_pipe.erase(iter);

			ret = true;

			break;
		}
	}

	if(true == ret)
	{
		char buf[8] = {0};

		struct client_pipe *pipe = (struct client_pipe *)handle_list[fd].p;

		::send(pipe->pipe_fd, buf, 1, 0);
	}

	return ret;
}

static void deinit_list(Lib_Epoll *epoll, int fd)
{
	handle_list[fd].type = CLIENT_TYPE_NORMAL;

	if(NULL != handle_list[fd].p)
	{
		free(handle_list[fd].p);
		handle_list[fd].p = NULL;
	}

	epoll->del(fd);

	close(fd);
	fd = -1;
}

static bool init_list(Lib_Epoll *epoll, int fd, enum client_type type)
{
	bool ret = false;

	if(CLIENT_TYPE_NORMAL == type)
	{
		goto out;
	}

	if(CLIENT_TYPE_NORMAL == handle_list[fd].type)
	{
		if(true != epoll->add(fd))
		{
			goto out1;
		}
	}

	handle_list[fd].type = type;

	switch(type)
	{
		case CLIENT_TYPE_MAIN:
			{
				handle_list[fd].p = calloc(1, sizeof(struct client_tcp_listener));
				if(NULL != handle_list[fd].p)
				{
					ret = true;
				}
			}
			break;
		case CLIENT_TYPE_SERV_1:
			{
				ret = true;
			}
			break;
		case CLIENT_TYPE_PIPE:
			{
				handle_list[fd].p = calloc(1, sizeof(struct client_pipe));
				if(NULL != handle_list[fd].p)
				{
					struct client_pipe *pipe = (struct client_pipe *)handle_list[fd].p;

					pipe->pipe_fd = -1;

					ret = true;
				}
			}
			break;
		default:
			{
				handle_list[fd].p = NULL;
				//ret = true;
			}
			break;
	}

	if(true == ret)
	{
		goto out;
	}

out1:
	if(0 <= fd)
	{
		close(fd);
		fd = -1;
	}
out:
	return ret;
}

static int listen_on(const char *ip, unsigned short port)
{
	int listen_fd = -1;

	struct sockaddr_in local_addr;

	listen_fd = socket(AF_INET, SOCK_STREAM, 0);

	if(0 > listen_fd)
	{
		goto out;
	}

	if(0 != Lib_IfConfig::setSocketReUseAddr(listen_fd))
	{
		goto out1;
	}

	memset(&local_addr, 0, sizeof(local_addr));

	local_addr.sin_family = AF_INET;
	local_addr.sin_port = htons(port);
	local_addr.sin_addr.s_addr = inet_addr(ip);

	if(0 != bind(listen_fd, (struct sockaddr *)&local_addr, sizeof(local_addr)))
	{
		goto out1;
	}

	if(0 != listen(listen_fd, 5))
	{
		goto out1;
	}

	goto out;

out1:
	if(0 <= listen_fd)
	{
		close(listen_fd);
		listen_fd = -1;
	}
out:
	return listen_fd;
}

static bool get_if_addr(const char *ifname, char *buffer, int size)
{
	in_addr_t ifaddr;

	if(0 == Lib_IfConfig::getInterfaceIpAddr(ifname, &ifaddr))
	{
		snprintf(buffer, size, "%u.%u.%u.%u", ((unsigned char *)&ifaddr)[0], \
												((unsigned char *)&ifaddr)[1], \
												((unsigned char *)&ifaddr)[2], \
												((unsigned char *)&ifaddr)[3]);

		return true;
	}

	return false;
}

static void handle_socket(void *arg, int fd)
{
	Lib_Epoll *epoll = (Lib_Epoll *)arg;

	switch(handle_list[fd].type)
	{
		case CLIENT_TYPE_MAIN:
			{

				int client_fd = accept(fd, NULL, NULL);

				if(0 <= client_fd)
				{
					if(true == init_list(epoll, client_fd, CLIENT_TYPE_PIPE))
					{
						printf("init_list -> CLIENT_TYPE_PIPE: %d, ok\n", client_fd);
					}
					else
					{
						printf("init_list -> CLIENT_TYPE_PIPE\n");
					}
				}
			}
			break;
		case CLIENT_TYPE_SERV_1:
			{
				struct sockaddr_in remote_addr;
				socklen_t len = sizeof(remote_addr);

				int client_fd = accept(fd, (struct sockaddr *)&remote_addr, &len);

				if(0 <= client_fd)
				{
					printf("accept: %d, %s:%d\n", client_fd, inet_ntoa(remote_addr.sin_addr), htons(remote_addr.sin_port));

					if(true == init_list(epoll, client_fd, CLIENT_TYPE_PIPE))
					{
						if(true == find_node("12345678", client_fd))
						{
							printf("init_list -> CLIENT_TYPE_PIPE: %d, ok\n", client_fd);
						}
						else
						{
							printf("find_node failed, close\n");

							deinit_list(epoll, client_fd);
						}
					}
					else
					{
						printf("init_list -> CLIENT_TYPE_PIPE\n");
					}
				}
			}
			break;
		case CLIENT_TYPE_PIPE:
			{
				char buffer[2048] = {0};

				int ret = ::recv(fd, buffer, sizeof(buffer), 0);

				if(0 < ret)
				{
					struct client_pipe *pipe = (struct client_pipe *)handle_list[fd].p;

					if(0 <= pipe->pipe_fd)
					{
						if(0 >= ::send(pipe->pipe_fd, buffer, ret, 0))
						{
							printf("send failed, close\n");

							deinit_list(epoll, fd);
							deinit_list(epoll, pipe->pipe_fd);
						}
					}
					else
					{
						struct pipe_node node;

						strncpy(node.xcode, buffer, sizeof(node.xcode));
						node.fd = fd;

						m_pipe.push_back(node);
					}
				}
				else
				{
					printf("recv failed, close\n");

					struct client_pipe *pipe = (struct client_pipe *)handle_list[fd].p;

					if(0 <= pipe->pipe_fd)
					{
						deinit_list(epoll, pipe->pipe_fd);
					}

					deinit_list(epoll, fd);
				}
			}
			break;
		default:
			{
				char buffer[2048] = {0};

				::recv(fd, buffer, sizeof(buffer), 0);

				printf("default fd, close!!!\n");

				deinit_list(epoll, fd);
			}
			break;
	}
}

int main(int argc, char **argv)
{
	char ifaddr[32] = {0};

	Lib_Epoll *epoll = NULL;

	int listen_fd = -1;

	memset(handle_list, 0, sizeof(handle_list));

	printf("start to get ip address for venet0:1\n");

	if(true != get_if_addr("venet0:0", ifaddr, sizeof(ifaddr)))
	{
		goto out;
	}

	printf("start to create epoll\n");

	epoll = Lib_Epoll::getObject(512);

	if(NULL == epoll)
	{
		goto out;
	}

	printf("start to listen......port 5173\n");

	listen_fd = listen_on(ifaddr, 5173);

	if(true != init_list(epoll, listen_fd, CLIENT_TYPE_MAIN))
	{
		goto out;
	}

	strncpy(((struct client_tcp_listener *)handle_list[listen_fd].p)->eth, "eth0", sizeof(((struct client_tcp_listener *)handle_list[listen_fd].p)->eth));
	strncpy(((struct client_tcp_listener *)handle_list[listen_fd].p)->ip, ifaddr, sizeof(((struct client_tcp_listener *)handle_list[listen_fd].p)->ip));
	((struct client_tcp_listener *)handle_list[listen_fd].p)->port = 5173;

	printf("start to listen......port 5174\n");

	listen_fd = listen_on(ifaddr, 5174);

	if(true != init_list(epoll, listen_fd, CLIENT_TYPE_SERV_1))
	{
		goto out;
	}

	while(1)
	{
		epoll->wait(1000, handle_socket, epoll);
	}

out:
	return 0;
}
